--[[
Name: targetAssist
Author: Sonora (The Dragonflight, US Kirin Tor)
Website & SVN: http://code.google.com/p/browniehelper/source/browse/#svn/trunk/targetAssist
License: GNU General Public License v3
]]

--Utility functions
local function keyFromValue(t, value)
    for k,v in pairs(t) do
        if v == value then return k end
    end
end

local function keys(t)
    ks = {}
    for k,_ in pairs(t) do table.insert(ks, k) end
    return ks
end

local function hasValue(t, value)
    for _, v in ipairs(t) do
        if v == value then return true end
    end
    for k, v in pairs(t) do
        if v == value then return true end
    end
end

local function tableIndex(t, value)
    for idx, v in ipairs(t) do
        if v == value then return idx end
    end
end

--Methods that draw stuff
function targetAssist:yesnoBox(msg, callback)
    StaticPopupDialogs["unitDoodles_YESNOBOX"] = {
        text = msg,
        button1 = "Yes",
        button2 = "No",
        OnAccept = function(self, data, data2)
            callback()
        end,
        timeout = 0,
        whileDead = true,
        hideOnEscape = true,
    }
    
    StaticPopup_Show ("unitDoodles_YESNOBOX")
end

function targetAssist:showHeaderMenu()

    local groupSelection = {}
    local unitNames = keys(self.roster)
    table.sort(unitNames)
    for _, unitName in ipairs(unitNames) do
        table.insert(groupSelection, {
            text = unitName,
            checked = function() return hasValue(self.config.targets, unitName) end,
            func = function(this, arg1, arg2, checked)
                if checked then
                    table.remove(self.config.targets, tableIndex(self.config.targets, unitName))
                    this.checked = false
                else
                    table.insert(self.config.targets, unitName)
                    this.checked = true
                end
                self:updateConfig()
            end,
        })
    end
    
    local broadcastSelection = {}
    for _, broadcast in ipairs(self.previousBroadcasts) do
        local sender, dist, targetList = unpack(broadcast)
        table.insert(broadcastSelection, {
                text = sender..' ('..table.concat(targetList, ", ")..')',
                notCheckable = true,
                func = function()
                    self.config.targets = targetList
                    self:updateConfig()
                end,
            }
        )
    end

    self.headerMenu = {
        {text = 'Main assists', 
            isTitle = true,
            notCheckable = true,
        },
    
        {text = 'Select',
            hasArrow = true,
            notCheckable = true,
            menuList = groupSelection,
        },
        {text = 'Select all',
            notCheckable = true,
            func = function()
                for _, unitName in ipairs(unitNames) do
                    if not hasValue(self.config.targets, unitName) then
                        table.insert(self.config.targets, unitName)
                    end
                end
                self:updateConfig()
            end,
        },
        {text = 'Deselect all',
            notCheckable = true,
            func = function()
                self.config.targets = {}
                self:updateConfig()
            end,
        },
        {text = 'Add party',
            notCheckable = true,
            func = function()
                for i=1,4 do
                    local unitName = keyFromValue(self.roster, 'party'..i)
                    if unitName and not hasValue(self.config.targets, unitName) then
                        table.insert(self.config.targets, unitName)
                    end
                end
                self:updateConfig()
            end,
        },        
        {text = 'Add tanks',
            notCheckable = true,
            func = function()
                self:addTanks()
                self:updateConfig()
            end,
        },
        {text = 'Add main assists',
            notCheckable = true,
            func = function()
                self:addMainAssists()
                self:updateConfig()
            end,
        },
    
    
        {text = 'Broadcasts', 
            isTitle = true,
            notCheckable = true,
        },
        {text = 'Recieved broadcasts',
            hasArrow = true,
            notCheckable = true,
            menuList = broadcastSelection,
        },
        {text = 'Broadcast targets',
            hasArrow = true,
            notCheckable = true,
            menuList = {
                {text = 'To raid',
                    notCheckable = true,
                    func = function() 
                        self:SendCommMessage('targetAssist.broadcastTargets', self:Serialize(self.config.targets), 'RAID')
                    end,
                },
                {text = 'To party',
                    notCheckable = true,
                    func = function() 
                        self:SendCommMessage('targetAssist.broadcastTargets', self:Serialize(self.config.targets), 'PARTY')
                    end,
                },
                {text = 'To guild',
                    notCheckable = true,
                    func = function() 
                        self:SendCommMessage('targetAssist.broadcastTargets', self:Serialize(self.config.targets), 'GUILD')
                    end,
                },
            },
        },
    
        {text = 'Configuration',
            isTitle = true,
            notCheckable = true,
        },
        {text = 'Open options panel',
            notCheckable = true,
            func = function() InterfaceOptionsFrame_OpenToCategory(self.optionsBaseFrame) end,
        },
    }

    -- Make the menu appear at the cursor: 
    EasyMenu(self.headerMenu, self.contextMenu, "cursor", 0 , 0, "MENU")
    
end

function targetAssist:showAssistFrameMenu(targetName)

    local idx = tableIndex(self.config.targets, targetName)
    local assistMenu = {
        {text = 'Move up',
            notCheckable = true,
            disabled = idx==1, 
            func = function()
                table.remove(self.config.targets, idx)
                table.insert(self.config.targets, idx-1, targetName)
                self:updateConfig()
            end,
        },
        {text = 'Move down',
            notCheckable = true,
            disabled = idx == #self.config.targets,
            func = function()
                table.remove(self.config.targets, idx)
                table.insert(self.config.targets, idx+1, targetName)
                self:updateConfig()
            end,
        },
    }
    
    EasyMenu(assistMenu, self.contextMenu, "cursor", 0 , 0, "MENU")
end

function targetAssist:showAssistTargetMenu(unitID)

    local currentIcon = GetRaidTargetIndex(unitID)

    local menu = {}
    for iconIdx, raidIcon in ipairs(self.config.raidIcons) do
        local iconName, iconTexture = unpack(raidIcon)
        table.insert(menu, {
                text = iconName,
                checked = iconIdx == currentIcon,
                func = function(this, arg1, arg2, checked)
                    if checked == true then
                        this.checked = true
                    else
                        this.checked = false
                    end
                    SetRaidTargetIcon(unitID, iconIdx)
                end,
                icon = iconTexture,
            }
        )
    
    end

    EasyMenu(menu, self.contextMenu, "cursor", 0 , 0, "MENU")

end

function targetAssist:createHeaderFrame()

    if not self.headerFrame then
        self.headerFrame = CreateFrame('Frame', 'targetAssistHeaderFrame', UIParent)
    end
    
    self.headerFrame:ClearAllPoints()
    self.headerFrame:SetWidth(self.config.headerWidth)
    self.headerFrame:SetHeight(self.config.headerHeight)
    self.headerFrame:SetPoint(unpack(self.config.headerAnchor))
    
    self.headerFrame:SetBackdrop(self.config.headerBackdrop)
    self.headerFrame:SetBackdropColor(0.15, 0.15, 0.15)
    
    if not self.headerFrame.texture then
        self.headerFrame.texture = self.headerFrame:CreateTexture()
    end
    self.headerFrame.texture:SetAllPoints(self.headerFrame)
    self.headerFrame.texture:SetTexture(self.config.headerTexture)
    self.headerFrame.texture:SetVertexColor(unpack(self.config.headerColor))
    
    self.headerFrame:EnableMouse(true)
    self.headerFrame:SetMovable(true)
    self.headerFrame:RegisterForDrag('LeftButton')
    self.headerFrame:SetScript("OnDragStart", function() 
        if self.config.headerUnlocked then self.headerFrame:StartMoving() end
    end)
    
    self.headerFrame:SetScript("OnDragStop", 
        function()
            self.headerFrame:StopMovingOrSizing()
            self.config.headerAnchor = {self.headerFrame:GetPoint()}
            self.config.headerAnchor[2] = 'UIParent'
        end
    )
    
    self.headerFrame:SetScript("OnMouseUp",
        function(_, button)
            if button == 'RightButton' then
                self:showHeaderMenu()
            end
        end
    )
    
    self.headerFrame:Show()
    
end

function targetAssist:createTargetButtons()
    
    local headerFrame = self.headerFrame

    --Clean out target button structures
    if self.targetButtons then
        for targetName, buttonFrame in pairs(self.targetButtons) do
            buttonFrame:Hide()
            buttonFrame:SetParent(nil)
        end
    end
    self.targetButtons = {}
    
    --For each assist target
    local anchorFrame, width = headerFrame, self.config.headerWidth/2
    self.buttonWidth = width-self.config.spacingOffset
    for _, targetName in ipairs(self.config.targets) do
    
        local unitID = self.roster[targetName]
        --TODO DEBUG CODE if not unitID and UnitIsUnit('player',targetName) then unitID = 'player' end
        if unitID then
    
            --Make the assist target unit button
            local assistFrame = CreateFrame("Button", nil, UIParent, "SecureUnitButtonTemplate")
            assistFrame:SetAttribute('unitName',targetName)
        
            assistFrame:SetPoint('TOPLEFT', anchorFrame, 'BOTTOMLEFT', 0, self.config.verticalOffset)
            assistFrame:SetWidth(self.buttonWidth)
            assistFrame:SetHeight(self.config.targetHeight)
            
            assistFrame:SetBackdrop(self.config.targetBackdrop)
            assistFrame:SetBackdropColor(0.15, 0.15, 0.15)
            
            local frameTexture = assistFrame:CreateTexture()
            --frameTexture:SetAllPoints(assistFrame)
            frameTexture:SetPoint('TOP', assistFrame, 'TOP')
            frameTexture:SetPoint('BOTTOM', assistFrame, 'BOTTOM')
            frameTexture:SetPoint('LEFT', assistFrame, 'LEFT')
            frameTexture:SetWidth(self.buttonWidth)
            frameTexture:SetTexture(self.config.targetTexture)
            frameTexture:SetVertexColor(unpack(self.config.targetColor))
            assistFrame.texture = frameTexture
        
            assistFrame.fontString = assistFrame:CreateFontString(nil, 'OVERLAY')
            assistFrame.fontString:SetFont(self.config.fontName, self.config.fontHeight)
            assistFrame.fontString:SetTextColor(unpack(self.config.fontColor))
            assistFrame.fontString:SetAllPoints(assistFrame)
        
            assistFrame:SetScript("OnMouseUp",
                function(_, button)
                    if button == 'RightButton' then
                        self:showAssistFrameMenu(targetName)
                    end
                end
            )
        
            assistFrame:SetAttribute('unitID', unitID)
            assistFrame.unitID = unitID
            SecureUnitButton_OnLoad(assistFrame, unitID, 'print("menu")')
        
            assistFrame:Show()
            self.targetButtons[targetName] = assistFrame
        
            --Make the assist target target unit button
            local targetFrame = CreateFrame("Button", nil, UIParent, "SecureUnitButtonTemplate")
        
            targetFrame:SetPoint('TOP', assistFrame, 'TOP')
            targetFrame:SetPoint('RIGHT', headerFrame, 'RIGHT')
            targetFrame:SetWidth(self.buttonWidth)
            targetFrame:SetHeight(self.config.targetHeight)
            
            targetFrame:SetBackdrop(self.config.targetBackdrop)
            targetFrame:SetBackdropColor(0.15, 0.15, 0.15)
            
            local frameTexture = targetFrame:CreateTexture()
            --frameTexture:SetAllPoints(targetFrame)
            frameTexture:SetPoint('TOP', targetFrame, 'TOP')
            frameTexture:SetPoint('BOTTOM', targetFrame, 'BOTTOM')
            frameTexture:SetPoint('LEFT', targetFrame, 'LEFT')
            frameTexture:SetWidth(self.buttonWidth)
            frameTexture:SetTexture(self.config.targetTexture)
            frameTexture:SetVertexColor(unpack(self.config.targetColor))
            targetFrame.texture = frameTexture

            targetFrame.fontString = targetFrame:CreateFontString(nil, 'OVERLAY')
            targetFrame.fontString:SetFont(self.config.fontName, self.config.fontHeight)
            targetFrame.fontString:SetTextColor(unpack(self.config.fontColor))
            targetFrame.fontString:SetAllPoints(targetFrame)
        
            targetFrame.raidIcon = targetFrame:CreateTexture()
            targetFrame.raidIcon:SetPoint('TOPLEFT', targetFrame, 'TOPRIGHT', 4, -1)
            targetFrame.raidIcon:SetHeight(self.config.targetHeight-2)
            targetFrame.raidIcon:SetWidth(self.config.targetHeight-2)
        
            targetFrame:SetScript("OnMouseUp",
                function(_, button)
                    if button == 'RightButton' then
                        self:showAssistTargetMenu(unitID..'target')
                    end
                end
            )
        
            targetFrame:SetAttribute('unitID', unitID..'target')
            targetFrame.unitID = unitID..'target'
            SecureUnitButton_OnLoad(targetFrame, unitID..'target', 'print("menu")')
        
            targetFrame:Show()        
            self.targetButtons[targetName..'target'] = targetFrame
            
            anchorFrame = assistFrame
    
        end
    
    end
    
end

function targetAssist:createBasePanel()

    return {
        type = "group",
        name = "targetAssist Configuration",
        args={
            introduction = {
                type = 'description',
                name = "Changes to many of the configuration settings for targetAssist can only be updated when you are out of combat.  If you make changes while in combat, they will be queued and applied later.\n\nIf you'd like to change the texture or font settings please make sure you have |cffFF7D0ASharedMedia|r installed.",
                order = 1
            },
        
            h1 = {
                type = 'header',
                name = 'Button Header',
                order = 20,
            },
            headerUnlocked = {
                type = 'toggle',
                name = 'Unlock header',
                desc = 'Allows the header bar to be moved by dragging it with the mouse.  Uncheck to lock it in place.',
                width = 'full',
                get = function() return self.config.headerUnlocked end,
                set = function(info, value) self.config.headerUnlocked = value end,
                order = 21,
            },
            headerWidth = {    
                type = 'input',
                name = 'Header width',
                desc = 'Width of the header bar (a positive value)',
                width = 'half',
                pattern = '%d',
                get = function() return tostring(self.config.headerWidth) end,
                set = function(info, value) 
                    self.config.headerWidth = tonumber(value)
                    self:updateConfig(true)
                end,
                order = 22,
            },
            headerHeight = {    
                type = 'input',
                name = 'Header height',
                desc = 'Height of the header bar (a positive value)',
                width = 'half',
                pattern = '%d',
                get = function() return tostring(self.config.headerHeight) end,
                set = function(info, value) 
                    self.config.headerHeight = tonumber(value)
                    self:updateConfig(true)
                end,
                order = 23,
            },
            headerTexture = {
                type = 'select',
                name = 'Header texture',
                desc = 'Texture used to paint the header bar.  If this control is greyed out please installed SharedMedia.',
                dialogControl = 'LSM30_Statusbar',
                values = AceGUIWidgetLSMlists.statusbar,
                get = function() return keyFromValue(AceGUIWidgetLSMlists.statusbar, self.config.headerTexture) end,
                set = function(info, key)
                    self.config.headerTexture = AceGUIWidgetLSMlists.statusbar[key]
                    self:updateConfig(true)
                end,
                order = 24,
            },
            headerColor = {
                type = 'color',
                name = 'Header color',
                desc = 'Changes the color of the header bar.',
                hasAlpha = true,
                get = function() return unpack(self.config.headerColor) end,
                set = function(info, r,g,b,a) 
                    self.config.headerColor = {r,g,b,a} 
                    self:updateConfig(true)
                end,
                order = 25,
            },
        
            h2 = {
                type = 'header',
                name = 'Assist Buttons',
                order = 30,
            },
            targetHeight = {    
                type = 'input',
                name = 'Button height',
                desc = 'Height of the assist buttons (a positive value)',
                width = 'half',
                pattern = '%d',
                get = function() return tostring(self.config.targetHeight) end,
                set = function(info, value) 
                    self.config.targetHeight = tonumber(value)
                    self:updateConfig(true)
                end,
                order = 32,
            },
            targetTexture = {
                type = 'select',
                name = 'Button texture',
                desc = 'Texture used to paint the assist buttons.  If this control is greyed out please installed SharedMedia.',
                dialogControl = 'LSM30_Statusbar',
                values = AceGUIWidgetLSMlists.statusbar,
                get = function() return keyFromValue(AceGUIWidgetLSMlists.statusbar, self.config.targetTexture) end,
                set = function(info, key)
                    self.config.targetTexture = AceGUIWidgetLSMlists.statusbar[key]
                    self:updateConfig(true)
                end,
                order = 34,
            },
            targetColor = {
                type = 'color',
                name = 'Button color',
                desc = 'Changes the color of the assist buttons.',
                hasAlpha = true,
                get = function() return unpack(self.config.targetColor) end,
                set = function(info, r,g,b,a) 
                    self.config.targetColor = {r,g,b,a} 
                    self:updateConfig(true)
                end,
                order = 35,
            },
            missingColor = {
                type = 'color',
                name = 'Button color (dead or missing)',
                desc = 'Color for buttons when the associated unit is dead or missing.',
                hasAlpha = true,
                width = 'full',
                get = function() return unpack(self.config.missingColor) end,
                set = function(info, r,g,b,a) 
                    self.config.missingColor = {r,g,b,a} 
                    self:updateConfig(true)
                end,
                order = 36,
            },
            myTargetColor = {
                type = 'color',
                name = 'Button color (my target)',
                desc = 'Color for buttons associated with the unit you currently have targeted.',
                hasAlpha = true,
                width = 'full',
                get = function() return unpack(self.config.myTargetColor) end,
                set = function(info, r,g,b,a) 
                    self.config.myTargetColor = {r,g,b,a} 
                    self:updateConfig(true)
                end,
                order = 37,
            },
        
            showHealth = {
                type = 'toggle',
                name = 'Show health',
                desc = 'If checked the colored texture on assist buttons will reflect the percentage health of the associated unit.',
                width = 'full',
                get = function() return self.config.showHealth end,
                set = function(info, value) 
                    self.config.showHealth = value
                    if value == false then
                        for _, buttonFrame in pairs(self.targetButtons or {}) do
                            buttonFrame.texture:SetWidth( self.buttonWidth )
                        end
                    end
                end,
                order = 38,
            },
        
            h3 = {
                type = 'header',
                name = 'Fonts & Style',
                order = 40,
            },
        
            fontName = {
                type = 'select',
                name = 'Font',
                desc = 'Font used to draw text in the addon.  If this box is greyed out please install SharedMedia.',
                dialogControl = 'LSM30_Font',
                values = AceGUIWidgetLSMlists.font,
                get = function() return keyFromValue(AceGUIWidgetLSMlists.font, self.config.fontName) end,
                set = function(info, key)
                    self.config.fontName = AceGUIWidgetLSMlists.font[key]
                    self:updateConfig(true)
                end,
                order = 41,
            },
            fontHeight = {    
                type = 'input',
                name = 'Font height',
                desc = 'Height of text drawn by the addon (a positive value)',
                width = 'half',
                pattern = '%d',
                get = function() return tostring(self.config.fontHeight) end,
                set = function(info, value) 
                    self.config.fontHeight = tonumber(value)
                    self:updateConfig(true)
                end,
                order = 42,
            },
            fontColor = {
                type = 'color',
                name = 'Font color',
                desc = 'Color for text drawn by the addon.',
                hasAlpha = true,
                get = function() return unpack(self.config.fontColor) end,
                set = function(info, r,g,b,a) 
                    self.config.fontColor = {r,g,b,a} 
                    self:updateConfig(true)
                end,
                order = 43,
            },
            spacingOffset = {    
                type = 'input',
                name = 'Horizontal spacing',
                desc = 'Horizontal spacing between assist buttons',
                pattern = '%d',
                get = function() return tostring(self.config.spacingOffset) end,
                set = function(info, value) 
                    self.config.spacingOffset = tonumber(value)
                    self:updateConfig(true)
                end,
                order = 44,
            },
            verticalOffset = {    
                type = 'input',
                name = 'Vertical spacing',
                desc = 'Vertical spacing between rows of assist buttons and the header bar.',
                pattern = '%d',
                get = function() return tostring(self.config.verticalOffset*-1) end,
                set = function(info, value) 
                    self.config.verticalOffset = tonumber(value)*-1
                    self:updateConfig(true)
                end,
                order = 45,
            },
        
            h5 = {
                type = 'header',
                name = 'Broadcasts and Target Updates',
                order = 10,
            },
            autoAcceptBroadcasts = {
                type = 'toggle',
                name = 'Accept all broadcasts',
                desc = 'Automatically accept incomming assist target lists broadcasted by other players in your party/group.  If this is left unchecked you will be asked whether or not you want to accept each broadcast individually.',
                width = 'full',
                get = function() return self.config.autoAcceptBroadcasts end,
                set = function(info, value) self.config.autoAcceptBroadcasts = value end,
                order = 11,
            },
            autoUpdateTanks = {
                type = 'toggle',
                name = 'Automatically add tanks',
                desc = 'Automatically add any party/raid members assigned the roles of tank or main tank.  If left unchecked you can manually add all tanks from the header bar context menu (right click).\n\nRaid leaders can set the main tank role with the "/mt PlayerName" chat command.',
                width = 'full',
                get = function() return self.config.autoUpdateTanks end,
                set = function(info, value) self.config.autoUpdateTanks = value end,
                order = 12,
            },
            autoUpdateMainAssists = {
                type = 'toggle',
                name = 'Automatically add main assists',
                desc = 'Automatically add any party/raid members assigned the role of main assist through the Blizzard UI.  If left unchecked you can manually add all main assists from the header bar context menu (right click).\n\nRaid leaders can set the main assist role with the "/ma PlayerName" chat command.',
                width = 'full',
                get = function() return self.config.autoUpdateMainAssists end,
                set = function(info, value) self.config.autoUpdateMainAssists = value end,
                order = 13,
            },
        
            h6 = {
                type = 'header',
                name = 'Performance',
                order = 60,
            },
            t1 = {
                type = 'description',
                name = 'It is recommended that you reload your user interface after making adjustments to the configuration.  This will help to free up memory and make the game more responsive.',
                order = 61,
            },
            reloadUI = {
                type = 'execute',
                name = 'Reload',
                desc = 'Reloads the WoW user interface.',
                func = function() ReloadUI() end,
                order = 62,
            },
            updateInterval = {    
                type = 'input',
                name = 'Update interval',
                desc = 'Time (in seconds) between updates of button state. Change this to a larger value if you are having performance problems (recommended values are 0.1-0.5).',
                width = 'half',
                pattern = '%d',
                get = function() return tostring(self.config.updateInterval) end,
                set = function(info, value) 
                    self.config.updateInterval = tonumber(value)
                    self:updateConfig(true)
                end,
                order = 63,
            },
            broadcastHistory = {
                type = 'input',
                name = 'Broadcast history',
                desc = 'Number of past broadcasts to save in the broadcast history.',
                width = 'full',
                pattern = '%d',
                get = function() return tostring(self.config.broadcastHistory) end,
                set = function(info, value) 
                    self.config.broadcastHistory = tonumber(value)
                    while #self.previousBroadcasts > self.config.broadcastHistory do
                        table.remove(self.broadcastHistory, 1)
                    end
                    self:updateConfig(true)
                end,
                order = 64,
            },
        
        
        }
    }

end




